package com.lyx.question.scheduled;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.lyx.common.constant.RedisContant;
import com.lyx.question.entity.Question;
import com.lyx.question.entity.QuestionRecord;
import com.lyx.question.mapper.QuestionMapper;
import com.lyx.question.mapper.QuestionRecordMapper;
import com.lyx.question.service.impl.QuestionServiceImpl;
import io.jsonwebtoken.lang.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author 14667
 * @date 2022/8/2 18:30
 */
@Component
public class ScheduleTask {
    private static final Logger logger = LoggerFactory.getLogger(ScheduleTask.class);
    @Autowired
    QuestionRecordMapper questionRecordMapper;

    @Autowired
    QuestionMapper questionMapper;

    @Autowired
    Executor asyncServiceExecutor;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    QuestionServiceImpl questionServiceImpl;

    @Scheduled(cron = "0 0 0 * * ?")
    @Async("asyncServiceExecutor")
    public void updateRank(){
        LocalDate now = java.time.LocalDate.now();
        LocalDate last = now.minusDays(1);
        LocalDate last2 = now.minusDays(2);
        LocalDate last7 = now.minusDays(7);
        LocalDate last8 = now.minusDays(8);
        Set<ZSetOperations.TypedTuple<String>> todayTotal = new HashSet<>();
        Set<ZSetOperations.TypedTuple<String>> lastTotal = new HashSet<>();
        //如果前2天的总排名为空，说明之前就没数据，只需要统计后面的
        if(redisTemplate.opsForZSet().size(RedisContant.QUESTION_NUM_WEEK_RANK + "_" +Objects.toString(last2))==0){
            //如果前一天不为空，将前一天的总的设为当天的，否则前一天就不放数据
            if(redisTemplate.opsForZSet().size(Objects.toString(last))>0){
                redisTemplate.opsForZSet().add(RedisContant.QUESTION_NUM_WEEK_RANK +"_" +Objects.toString(last),redisTemplate.opsForZSet().rangeWithScores(Objects.toString(last),0,-1));
                redisTemplate.expire(RedisContant.QUESTION_NUM_WEEK_RANK + "_"+Objects.toString(last),8, TimeUnit.DAYS);
            }
        }else{
            //先把前一天的总排名清空，准备重新计算
            getNewTotal(last, last2, last8, lastTotal);
        }
        //进行到这一步  ，前一天的总的一定就是正确的了，当然前一天可能也没有数据
        getNewTotal(now,last,last7,todayTotal);

    }

    @Scheduled(cron = "0 0 0 * * ?")
    @Async("asyncServiceExecutor")
    public void updateCorrectRate(){
        logger.info("start executeAsync");
        String date = java.time.LocalDate.now().minusDays(1).toString();
        String start  = date+" 00:00:00";
        String end = date+" 23:59:59";
        //按question_id分组，查询出当天对应题目的正确数量和错误数量，然后更新到数据库中

        //先查询出所有的当天做题记录的数量
        Integer count = questionRecordMapper.getDayRecordsNum(start,end);
        //开4个或者5个线程去执行任务
        for(int i = 0,j = count/4;j<=count-count%4;j+=count/4){

            int finalI = i;
            int finalJ = j-1;
            asyncServiceExecutor.execute(()->{
                Integer from = finalI;
                Integer to = finalJ;
                List<Question> questionList = questionRecordMapper.getCorrectWrongQuestionNum(start,end,from,to);
                //如果集合为空就不进行更新
                if (Collections.isEmpty(questionList)){
                    return ;
                }
                //根据id更新
                boolean flag = questionMapper.batchUpdateQuestionById(questionList);
                if(!flag){
                    logger.error(date+"定时更新题目正确率失败");
                }
                logger.info("end executeAsync");
            });
            i = j;
        }
        if(count%4!=0){
            asyncServiceExecutor.execute(()->{
                Integer from = count-count%4;
                Integer to = count-1;
                List<Question> questionList = questionRecordMapper.getCorrectWrongQuestionNum(start,end,from,to);
                //如果集合为空就不进行更新
                if (Collections.isEmpty(questionList)){
                    return ;
                }
                //根据id更新
                boolean flag = questionMapper.batchUpdateQuestionById(questionList);
                if(!flag){
                    logger.error(date+"定时更新题目正确率失败");
                }
                logger.info("end executeAsync");
            });
        }

    }

    private void getNewTotal(LocalDate last, LocalDate last2, LocalDate last8,  Set<ZSetOperations.TypedTuple<String>> lastTotal) {
        redisTemplate.opsForZSet().removeRange(RedisContant.QUESTION_NUM_WEEK_RANK+"_"+Objects.toString(last),0,-1);
        Set<ZSetOperations.TypedTuple<String>> day8Set = redisTemplate.opsForZSet().rangeWithScores(Objects.toString(last8), 0, -1);
        Set<ZSetOperations.TypedTuple<String>> lastDayTotalSet = redisTemplate.opsForZSet().rangeWithScores(RedisContant.QUESTION_NUM_WEEK_RANK +"_"+ Objects.toString(last2), 0, -1);
        if(!CollectionUtils.isEmpty(lastDayTotalSet)){
            if(!io.jsonwebtoken.lang.Collections.isEmpty(day8Set)){
                ImmutableMap<String, ZSetOperations.TypedTuple<String>> day8Map = Maps.uniqueIndex(day8Set, ZSetOperations.TypedTuple::getValue);
                for (ZSetOperations.TypedTuple<String> stringTypedTuple : lastDayTotalSet) {
                    String value = stringTypedTuple.getValue();
                    Double score = stringTypedTuple.getScore();
                    Double score1 = day8Map.getOrDefault(value, new DefaultTypedTuple<String>(value, 0d)).getScore();
                    DefaultTypedTuple<String> objectDefaultTypedTuple = new DefaultTypedTuple<>(value, score - score1);
                    lastTotal.add(objectDefaultTypedTuple);
                }
                redisTemplate.opsForZSet().add(RedisContant.QUESTION_NUM_WEEK_RANK +"_" +Objects.toString(last), lastTotal);
                redisTemplate.expire(RedisContant.QUESTION_NUM_WEEK_RANK +"_"+ Objects.toString(last),8, TimeUnit.DAYS);

            }else{
                redisTemplate.opsForZSet().add(RedisContant.QUESTION_NUM_WEEK_RANK +"_" +Objects.toString(last),lastDayTotalSet);
                redisTemplate.expire(RedisContant.QUESTION_NUM_WEEK_RANK +"_"+ Objects.toString(last),8, TimeUnit.DAYS);
            }
        }
        //加上当天的 如果有数据的话
        if(redisTemplate.opsForZSet().size(Objects.toString(last))>0){
            Set<ZSetOperations.TypedTuple<String>> lastSet = redisTemplate.opsForZSet().rangeWithScores(Objects.toString(last), 0, -1);
            for (ZSetOperations.TypedTuple<String> stringTypedTuple : lastSet){
                String value = stringTypedTuple.getValue();
                Double score = stringTypedTuple.getScore();
                redisTemplate.opsForZSet().incrementScore(RedisContant.QUESTION_NUM_WEEK_RANK +"_"+ Objects.toString(last),value,score);
            }
        }
    }
}
